/*
 * @Copyright: Citylife 2021
 * @Author: shaokui.gu
 * @Date: 2021-02-18 15:28:19
 * @Description: 
 */
import 'dart:convert';
import 'dart:developer';
import 'dart:io';

import 'package:connectivity/connectivity.dart';
import 'package:dio/adapter.dart';
import 'package:dio/dio.dart';
import 'package:dio_http_cache/dio_http_cache.dart';
import 'package:logger/logger.dart';

import '../../config/config.dart';
import 'Interceptors/network_request_interceptor.dart';
import 'Interceptors/network_response_interceptor.dart';
import 'network_common.dart';
import 'network_config.dart';
import 'network_delegate.dart';
import 'network_exception.dart';

//
class NetworkUtil {
  Dio _dio;
  static final logger = Logger();
  static final NetworkUtil _netUtil = NetworkUtil._internal();

  NetworkUtil._internal() {
    /// setup
    BaseOptions options = new BaseOptions(
      baseUrl: Config.baseUrl,
      connectTimeout: NetworkConfig.instance.connectTimeout,
      receiveTimeout: NetworkConfig.instance.receiveTimeout,
    );
    _dio = new Dio(options);
    (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
        (client) {
      client.badCertificateCallback = (cert, host, port) {
        return true;
      };
    };

    /// 如果支持http2.0 可以启用http2.0插件
    // _dio.httpClientAdapter = Http2Adapter(
    //   ConnectionManager(idleTimeout: 10000),
    // );

    _dio.interceptors.add(
      LogInterceptor(
        requestBody: true,
        responseBody: true,
      ),
    );
    _dio.interceptors.addAll([
      NetworkRequestInterceptor(),
      NetworkResponseInterceptor(),
    ]);
    _dio.interceptors.add(
      DioCacheManager(
        CacheConfig(
          baseUrl: NetworkConfig.instance.baseUrl,
        ),
      ).interceptor,
    );
  }

  static NetworkUtil get instance => _netUtil;

  /// 处理异常
  void _handleException(
    NetworkDelegate delegate,
    Exception exception,
    bool isLastRequest,
  ) {
    /// TODO: 定义一些特殊类型的exception用来处理特殊逻辑。如token过期
    switch (exception.runtimeType) {
      default:
    }

    if (delegate != null) {
      delegate.onException(exception, isLastRequest);
    } else {
      throw exception;
    }
  }

  /// 发送Post请求
  Future<dynamic> get<T>(String url) async {
    return await _dio.get(url);
  }

  /// 发送Post请求
  /// url : 请求地址
  /// delegate : 回调主体
  /// key : 请求的key
  /// isLastRequest: 是否为最后一次请求，处理loading
  /// isHasCacheData: 是否需要缓存
  /// networkAccessType: 缓存策略
  /// headers: 请求头
  /// cacheKeys: 保存缓存数据的key
  /// isList: 返回的数据是否是列表
  /// responseHandler: 请求回调
  Future<dynamic> sendRequest<T>(
    String url, {
    NetworkRequestMethd method,
    NetworkDelegate delegate,
    String key,
    bool isLastRequest = true,
    bool isHasCacheData = false,
    NetworkAccessType networkAccessType = NetworkAccessType.Both,
    Map<String, dynamic> parameters,
    Map<String, String> headers,
    List<String> cacheKeys,
    bool isList = false,
    dynamic Function(dynamic value) responseHandler,
  }) async {
    // 如果为网络状态为无网并且读取网络数据的话直接返回
    var connectivityResult = await (Connectivity().checkConnectivity());
    if (connectivityResult == ConnectivityResult.none &&
        networkAccessType == NetworkAccessType.NetworkOnly) {
      /// 抛出无网络的exception
      _handleException(
          delegate,
          NoNetworkException(key, false, isHasCacheData: isHasCacheData),
          isLastRequest);
      return;
    }

    /// 初始options 在发送请求之前可以在dio的拦截其中进行修改
    Options options = Options(
      contentType: NetworkConfig.instance.contentType,
    );

    /// 是否使用缓存
    bool isUseCacheData = (networkAccessType == NetworkAccessType.Both ||
        networkAccessType == NetworkAccessType.CacheOnly);
    if (cacheKeys != null) {
      String subKey;
      if (cacheKeys.length > 0) {
        subKey = cacheKeys.join("/");
      }

      /// dio的缓存插件配置
      options = buildCacheOptions(
        Duration(
          days: NetworkConfig.instance.cacheMaxAge,
        ),
        options: options,
        subKey: subKey,
        forceRefresh: !isUseCacheData,
      );
    }
    log('接口名字：$url\n请求参数：${parameters ?? '{}'}', name: LOG_NAME);

    /// 通知代理开始请求数据
    delegate?.onSendRequest(key);
    try {
      Response response;
      switch (method) {
        case NetworkRequestMethd.Post:
          {
            response = await _dio.post(url,
                data: json.encode(parameters),
                options: options, onSendProgress: (int current, int totoal) {
              delegate?.onSendProgressChanged(current / totoal, key);
            }, onReceiveProgress: (int current, int totoal) {
              delegate?.onReceiveProgressChanged(current / totoal, key);
            });
            break;
          }
        case NetworkRequestMethd.Patch:
          {
            response = await _dio.patch(url,
                data: json.encode(parameters),
                options: options, onSendProgress: (int current, int totoal) {
              delegate?.onSendProgressChanged(current / totoal, key);
            }, onReceiveProgress: (int current, int totoal) {
              delegate?.onReceiveProgressChanged(current / totoal, key);
            });
            break;
          }
        case NetworkRequestMethd.Delete:
          {
            response = await _dio.delete(url,
                data: json.encode(parameters), options: options);
            break;
          }
        default:
          {
            response = await _dio.get(url,
                queryParameters: parameters,
                options: options, onReceiveProgress: (int current, int totoal) {
              delegate?.onReceiveProgressChanged(current / totoal, key);
            });
          }
      }

      bool isCacheDataFromResponse =
          /*response.extra[DIO_CACHE_KEY_TRY_CACHE] ??*/ false;
      if (response.statusCode == SKIP_STATUS_CODE) {
        /// 通知代理跳过处理，可能需要处理loading
        delegate?.onSkipHandleResult(key, isLastRequest);
      } else if (response.statusCode == 200) {
        /// 请求返回200 表示成功，处理返回内容
        int returnCode = response.statusCode;
        log('接口名字：$url \n返回结果：$response \n是否缓存：${isCacheDataFromResponse ? '是' : '否'}',
            name: LOG_NAME);
        switch (returnCode) {
          case 200:
            var responseData = response.data;

            /// TODO: 处理返回的数据
            dynamic obj;

            ///因为产品无网逻辑为：如果缓存数据为空，并且无网需要展示无网页面，这里如果出现这种情况则isHasCatch为NO
            bool isHasCatch = true;
            if (responseHandler != null) {
              if (isList) {
                List<T> result = [];
                for (dynamic item in responseData) {
                  result.add(responseHandler(item));
                }
                isHasCatch = !(result.length == 0);
                obj = result;
              } else {
                obj = responseHandler(responseData);
              }
            } else {
              obj = responseData;
            }
            delegate?.onBusinessResult(
              obj,
              key,
              isCacheDataFromResponse,
              isLastRequest,
              isCacheDataFromResponse
                  ? NetworkAccessType.CacheOnly
                  : NetworkAccessType.NetworkOnly,
            );

            /// 如果缓存策略使用了缓存那么第一次加载的是缓存，在进行第二次请求使用 [NetworkAccessType.NetworkOnly]
            if (networkAccessType == NetworkAccessType.Both &&
                cacheKeys != null) {
              sendRequest<T>(
                url,
                method: method,
                delegate: delegate,
                isList: isList,
                key: key,
                isLastRequest: isLastRequest,
                isHasCacheData: isHasCatch,
                networkAccessType: NetworkAccessType.NetworkOnly,
                parameters: parameters,
                headers: headers,
                cacheKeys: cacheKeys,
                responseHandler: responseHandler,
              );
            }

            return obj;
            break;

          /// 普通业务异常处理
          default:
            if (isCacheDataFromResponse == false) {
              /// 缓存错误不抛异常
              throw BusinessException(
                returnCode,
                response.data["messages"][0]["message"],
                response.data["messages"][0]["code"],
                key,
                isUseCacheData,
              );
            }
            break;
        }

        /// 如果缓存策略使用了缓存那么第一次加载的是缓存，在进行第二次请求使用 [NetworkAccessType.NetworkOnly]
        if (networkAccessType == NetworkAccessType.Both && cacheKeys != null) {
          sendRequest<T>(
            url,
            method: method,
            delegate: delegate,
            isList: isList,
            key: key,
            isLastRequest: isLastRequest,
            networkAccessType: NetworkAccessType.NetworkOnly,
            parameters: parameters,
            headers: headers,
            cacheKeys: cacheKeys,
            responseHandler: responseHandler,
          );
        }
      } else {
        /// 其他的当做异常处理
        _handleException(
            delegate, CommonException(key, isUseCacheData), isLastRequest);
      }
    } on DioError catch (e) {
      logger.e("net ---- DioError", e);
      if (connectivityResult == ConnectivityResult.none) {
        _handleException(
            delegate,
            NoNetworkException(key, isUseCacheData,
                isHasCacheData: isHasCacheData),
            isLastRequest);
      } else {
        _handleException(
            delegate, CommonException(key, isUseCacheData), isLastRequest);
      }
    } on Error catch (e) {
      logger.e("net ---- Error", e);
      _handleException(delegate, Exception(e.toString()), isLastRequest);
    } catch (e) {
      logger.e("net ---- Other Error", e);
      _handleException(delegate, e, isLastRequest);
    }
  }
}
